/*
#
#  Copyright 2011 Matthew Jannace
#
#  This file is part of RoboCode BlackHoleAgent
#
#  RoboCode BlackHoleAgent is free software: you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation, either version 3 of the
#  License, or (at your option) any later version.
#
#  RoboCode BlackHoleAgent is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with RoboCode BlackHoleAgent. If not, see
#  <http://www.gnu.org/licenses/>.
#
 */
package com.jannaceltd.TeamBlackHoleBeer.Modules.Radar;

import com.jannaceltd.TeamBlackHoleBeer.BlackHole_Abstract;
import com.jannaceltd.TeamBlackHoleBeer.BlackHole_Interface;
import com.jannaceltd.TeamBlackHoleBeer.Modules.HelperClasses.Enemy;
import com.jannaceltd.TeamBlackHoleBeer.Modules.HelperClasses.Robots;
import com.jannaceltd.TeamBlackHoleBeer.Modules.HelperClasses.TeamMate;
import com.jannaceltd.TeamBlackHoleBeer.Utilities.MathUtils;
import robocode.RobotDeathEvent;
import robocode.ScannedRobotEvent;
import robocode.util.Utils;

/**
 * Class that Controls the Robots Radar
 * @author Matthew Jannace <mjannace@gmail.com>
 * @version 1.0
 */
public class Radar implements BlackHole_Interface {

    private BlackHole_Abstract Agent;
    private Robots bot;
    private RadarDirection currentRadarDirection = RadarDirection.RADAR_NODIR;
    private boolean wasClosest = false;
    /**
     * State of Radar Lock Protocol
     */
    public boolean isRadarLockOn = false;

    /**
     * Creates a Radar Object
     * @param TeamAgent 
     */
    public Radar(BlackHole_Abstract TeamAgent) {
        this.Agent = TeamAgent;
    }

    /**
     * Performs a Radar Scan
     */
    public void doRadarScan() {

        while (Agent.getTime() < 9) {
            activateRadarSweep(pickRandomkDirection());
            Agent.execute();
        }

        isRadarLockOn = false;

        if (Agent.strategy.amIClosestToTarget() && (Agent.teamates.size() > 0 || Agent.getOthers() == 1)) {
            wasClosest = true;
            double ElapseSinceLock = (Agent.getTime() - Agent.target.getCtime());
            if (ElapseSinceLock < 16) {
                isRadarLockOn = true;
                activateRadarLock();
            } else {
                currentRadarDirection = RadarDirection.RADAR_NODIR;
                activateRadarSweep(pickRandomkDirection());
            }
        } else {
            if (wasClosest == true) {
                wasClosest = false;
                currentRadarDirection = RadarDirection.RADAR_NODIR;
            }
            activateRadarSweep(pickRandomkDirection());
        }
    }

    /**
     * Pick Random Direction for the Agent to rotate his radar
     */
    private RadarDirection pickRandomkDirection() {
        if (currentRadarDirection == RadarDirection.RADAR_NODIR) {
            if (Math.random() < .6) {
                currentRadarDirection = RadarDirection.RADAR_RIGHT;
                return RadarDirection.RADAR_RIGHT;
            } else {
                currentRadarDirection = RadarDirection.RADAR_LEFT;
                return RadarDirection.RADAR_LEFT;
            }
        } else {
            return currentRadarDirection;
        }
    }

    /**
     * Performs a Radar Lock on the Target
     *
     * Based on the Radar Tutorial
     * @link http://robowiki.net/wiki/One_on_One_Radar
     */
    private void activateRadarLock() {

        double RadarFactor = 1.9;
        double radarTurn =
                // Absolute bearing to target
                Agent.getHeadingRadians() + Agent.target.getBearingRadians()
                // Subtract current radar heading to get turn required
                - Agent.getRadarHeadingRadians();

        Agent.setTurnRadarRightRadians(RadarFactor * Utils.normalRelativeAngle(radarTurn));
    }

    /**
     * Performs a Spin Radar Sweep
     * @param direction
     *
     * Based on the Radar Tutorial
     * @link http://robowiki.net/wiki/One_on_One_Radar
     */
    public void activateRadarSweep(RadarDirection direction) {
        switch (direction) {
            case RADAR_LEFT:
                //System.out.println("Radar Left");
                Agent.setTurnRadarLeftRadians(Double.POSITIVE_INFINITY);
                break;
            case RADAR_RIGHT:
                //System.out.println("Radar Right");
                Agent.setTurnRadarRightRadians(Double.POSITIVE_INFINITY);
                break;
            default:
                Agent.setTurnRadarRightRadians(Double.POSITIVE_INFINITY);
        }
    }

    /**
     * Updated Enemies and Teammates Hashmaps to set the Dead Robots Entries to not Alive
     * @param e
     */
    public void doOnRobotDeath(RobotDeathEvent e) {

        if (Agent.enemies.get(e.getName()) != null) {
            Agent.enemies.get(e.getName()).setAlive(false);
        }
        if (Agent.teamates.get(e.getName()) != null) {
            Agent.teamates.remove(e.getName());
        }
        if (Agent.teamatesNextLocation.get(e.getName()) != null) {
            Agent.teamatesNextLocation.remove(e.getName());
        }
        Agent.strategy.ChooseTarget();
    }

    /**
     * Updated Enemies and Teammates Hashmaps with the Scanned Information
     * @param event
     */
    public void doOnScannedRobot(ScannedRobotEvent event) {
        // Check If Scanned Bot is a TeamMate
        if (Agent.isTeammate(event.getName())) {
            ProcessTeamMateRobot(event);
        } else {
            ProcessEnemyRobot(event);
            Agent.comm.doBroadcastEnemyLocation((Enemy) bot);
        }
    }

    /**
     * Update Enemies Hashmaps with the Scanned Information
     * @param event
     */
    protected void ProcessEnemyRobot(ScannedRobotEvent event) {
        bot = Agent.enemies.get(event.getName());
        if (bot == null) {
            bot = new Enemy();
            Agent.enemies.put(event.getName(), (Enemy) bot);
        }
        ProcessRobot(event);
    }

    /**
     * Update Teammates Hashmaps with the Scanned Information
     * @param event
     */
    protected void ProcessTeamMateRobot(ScannedRobotEvent event) {
        bot = Agent.teamates.get(event.getName());
        if (bot == null) {
            bot = new TeamMate(Agent);
            Agent.teamates.put(event.getName(), (TeamMate) bot);
        }
        ProcessRobot(event);
    }

    /**
     * Updated Robots Hashmaps with the Scanned Information
     * @param event
     */
    protected void ProcessRobot(ScannedRobotEvent event) {
        bot.setName(event.getName());
        bot.setEnergy(event.getEnergy());
        bot.setAlive(true);
        bot.setBearingRadians(event.getBearingRadians());
        bot.setHeadingRadians(event.getHeadingRadians());
        bot.setVelocity(event.getVelocity());
        bot.setDistance(event.getDistance());
        bot.setPos(MathUtils.project(Agent.getPos(), Agent.getHeadingRadians() + event.getBearingRadians(), event.getDistance()));
        bot.setChangehead(Utils.normalNearAbsoluteAngle(event.getHeadingRadians() - bot.getHeadingRadians()) / (Agent.getTime() - bot.getCtime()));
        bot.setCtime(Agent.getTime());
    }
}
